/** @file    BusRing.cpp
 *  @author  Alessandro Polo
 *  @version $Id: BusRing.cpp 2890 2010-08-12 01:57:23Z alex $
 *  @brief
 * File containing methods for the wosh::BusRing class.
 * The header for this class can be found in BusRing.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

 #include <core/BusRing.h>
 #include <core/BusManager.h>
 #include <core/ObjectFactory.h>


using namespace std;
namespace wosh {

 WOSH_REGISTER(wosh::BusRing, "wosh::Bus", 1.01, _static_BusRing)

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS

BusRing::BusRing( const std::string& busname )
	: Bus(), ThreadImpl(NULL, "wosh.Bus.BusRing") {
	if ( !busname.empty() )
		Object::setName( busname );
	 else
		 Object::setName( _BusRing_NAME, getBusID() );
	Log.setContext( Object::getName() );
	Log.setLevel( LOG_INFO );

	this->threadname = Object::getName();

	push_event( Bus::STATE_CREATED );
 }

BusRing::~BusRing() {
	Log(LOG_VERBOSE, " Destroying.." );
	if ( isBusRunning() )
		stopBus();

	forward_event_to_listeners( Bus::STATE_DESTROYED );

	// message queue free messages when destroyed

	Log(LOG_DEBUG, ":~BusRing() Destroyed." );
 }

//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////// BUS OPERATIONS

WRESULT BusRing::startBus( unsigned long timeout ) {
	if ( isBusRunning() ) {
		Log(LOG_WARNING, ":startBus() : FAILED: Bus is already Running." );
		return WRET_ERR_ILLEGAL_USE;
	 }
	Log(LOG_INFO, ":startBus(%lu) Starting Bus..", timeout );
	return startThread(timeout);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT BusRing::stopBus( unsigned long timeout ) {
	if ( !isBusRunning() ) {
		Log(LOG_WARNING, ":stopBus() : FAILED: Bus is not Running." );
		return WRET_ERR_ILLEGAL_USE;
	 }
	Log(LOG_INFO, ":stopBus(%lu) : Stopping Bus..", timeout );
	return quitThread(timeout);
 }

////////////////////////////////////////////////////////////////////////////////////////////// BUS OPERATIONS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////// THREAD RUN

void BusRing::runThread() {
	forward_event_to_listeners( Bus::STATE_STARTED );
	Log(LOG_INFO, ":runThread() : Bus Started.." );
	processingInitialize();
	while( this->running ) {
		// fix deferred (un)registration of handlers
		// as this to prevent deadlocks when connecting bus WITHIN busMessage() call
		fixDeferredListeners();
		if ( this->msgQueue.isEmpty() ) {
			ThreadImpl::sleepForMSec(75);
			setThreadAlive();
			continue;
		 }
		ThreadImpl::sleepForMSec(50);
		setThreadAlive();
		processingStarted();
		 // get and remove it from queue
		Message* message = this->msgQueue.pop();
		if ( message == NULL ) {
			processingFinished();
			continue;
		 }
		Log(LOG_INFO, " MSG %"PRId64" %s->[%s]%s | %s", message->getID(), 
				message->getSource().toString().c_str(), message->getDestinationBus().c_str(), message->getDestination().toString().c_str(),
				message->getContentClassName().c_str()	);

		// forward message to handlers (message is passed as constant)
		if ( isForMyself(message) )
			forward_message_to_listeners( message );
		// forward message to the Bus Manager, it will route message (if required)
		push_message( message );
		// finally delete the instance
		delete message;
		processingFinished();
	 }
	Log(LOG_INFO, ":runThread() : Bus Stopped.." );
	forward_event_to_listeners( Bus::STATE_STOPPED );
 }

////////////////////////////////////////////////////////////////////////////////////////////////// THREAD RUN
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void BusRing::crashDetected( double probability ) {
	Log(LOG_CRITICAL, ":crashDetected(%f) : MsgID#%"PRId64", Listener: %s", probability, this->cDebug.getMessageId(), this->cDebug.getCurrentListener().c_str() );
	// add mutex !
 }

/////////////////////////////////////////////////////////////////////////////////////////////////// MESSAGING

void BusRing::forward_message_to_listeners( const Message* message ) {
	if ( cDebug.isEnabled() ) {
		cDebug.setCurrentListener(NULL);
		cDebug.setCurrentMessage(message);
	 }
	if ( message == NULL ) return;
	this->nodes.transactionBeginRead();
	tIBusListenerList::Iterator it;
	const tIBusListenerList::ConstIterator it_end = this->nodes.end();
	for ( it=this->nodes.begin(); it!=it_end; ++it ) {
		if ( *it == NULL ) continue;
		if ( cDebug.isEnabled() )
			cDebug.setCurrentListener(*it);
		if ( *it == NULL ) continue;
		(*it)->busMessage( *message, this );
	 }
	this->nodes.transactionEnd();
	if ( cDebug.isEnabled() ) {
		cDebug.setCurrentListener(NULL);
		cDebug.setCurrentMessage(NULL);
	 }
 }

void BusRing::forward_event_to_listeners( Bus::BUS_STATE state ) {
	this->nodes.transactionBeginRead();
	tIBusListenerList::Iterator it;
	const tIBusListenerList::ConstIterator it_end = this->nodes.end();
	for ( it=this->nodes.begin(); it!=it_end; ++it ) {
		if ( *it == NULL ) continue;
		(*it)->busEvent( state, this );
	 }
	this->nodes.transactionEnd();
 }

/////////////////////////////////////////////////////////////////////////////////////////////////// MESSAGING
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void BusRing::setParentObject( const Object* parent ) {
	Object::setParentObject(parent);
	Thread::setOwnerObject(parent);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

}; // namespace wosh
